package org.kc7bfi.jflac.io;

/**
 * libFLAC - Free Lossless Audio Codec library Copyright (C) 2000,2001,2002,2003
 * Josh Coalson
 * 
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Library General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option) any
 * later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Library General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

import java.io.IOException;
import java.io.OutputStream;

import org.kc7bfi.jflac.util.CRC16;
import org.kc7bfi.jflac.util.CRC8;

/**
 * Bit-wide output stream.
 * 
 * @author kc7bfi
 */
public class BitOutputStream {
	private static final int BITS_PER_BLURB = 8;
	private static final int BITS_PER_BLURB_LOG2 = 3;
	private static final int BYTES_PER_BLURB = 1;
	private static final byte BLURB_TOP_BIT_ONE = ((byte) 0x80);
	private static final long[] MASK32 = new long[] { 0, 0x0000000000000001, 0x0000000000000003, 0x0000000000000007, 0x000000000000000F, 0x000000000000001F, 0x000000000000003F, 0x000000000000007F,
			0x00000000000000FF, 0x00000000000001FF, 0x00000000000003FF, 0x00000000000007FF, 0x0000000000000FFF, 0x0000000000001FFF, 0x0000000000003FFF, 0x0000000000007FFF, 0x000000000000FFFF,
			0x000000000001FFFF, 0x000000000003FFFF, 0x000000000007FFFF, 0x00000000000FFFFF, 0x00000000001FFFFF, 0x00000000003FFFFF, 0x00000000007FFFFF, 0x0000000000FFFFFF, 0x0000000001FFFFFF,
			0x0000000003FFFFFF, 0x0000000007FFFFFF, 0x000000000FFFFFFF, 0x000000001FFFFFFF, 0x000000003FFFFFFF, 0x000000007FFFFFFF, 0x00000000FFFFFFFF, 0x00000001FFFFFFFFL, 0x00000003FFFFFFFFL,
			0x00000007FFFFFFFFL, 0x0000000FFFFFFFFFL, 0x0000001FFFFFFFFFL, 0x0000003FFFFFFFFFL, 0x0000007FFFFFFFFFL, 0x000000FFFFFFFFFFL, 0x000001FFFFFFFFFFL, 0x000003FFFFFFFFFFL, 0x000007FFFFFFFFFFL,
			0x00000FFFFFFFFFFFL, 0x00001FFFFFFFFFFFL, 0x00003FFFFFFFFFFFL, 0x00007FFFFFFFFFFFL, 0x0000FFFFFFFFFFFFL, 0x0001FFFFFFFFFFFFL, 0x0003FFFFFFFFFFFFL, 0x0007FFFFFFFFFFFFL, 0x000FFFFFFFFFFFFFL,
			0x001FFFFFFFFFFFFFL, 0x003FFFFFFFFFFFFFL, 0x007FFFFFFFFFFFFFL, 0x00FFFFFFFFFFFFFFL, 0x01FFFFFFFFFFFFFFL, 0x03FFFFFFFFFFFFFFL, 0x07FFFFFFFFFFFFFFL, 0x0FFFFFFFFFFFFFFFL, 0x1FFFFFFFFFFFFFFFL,
			0x3FFFFFFFFFFFFFFFL, 0x7FFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL };

	private byte[] buffer = new byte[0];
	private int outCapacity = 0; // in blurbs
	private int outBlurbs = 0;
	private int outBits = 0;
	private int totalBits = 0; // must always == BITS_PER_BLURB*blurbs+bits
	private int consumedBlurbs = 0;
	private int consumedBits = 0;
	private int totalConsumedBits = 0;
	private short readCRC16 = 0;
	private OutputStream os;

	/*
	 * WATCHOUT: The current implentation is not friendly to shrinking, i.e. it
	 * does not shift left what is consumed, it just chops off the end, whether
	 * there is unconsumed data there or not. This is OK because currently we
	 * never shrink the buffer, but if this ever changes, we'll have to do some
	 * fixups here.
	 */
	private boolean resize(int newCapacity) {
		if (outCapacity >= newCapacity)
			return true;
		byte[] newBuffer = new byte[newCapacity];
		System.arraycopy(buffer, 0, newBuffer, 0, Math.min(outBlurbs + ((outBits != 0) ? 1 : 0), newCapacity));
		if (newCapacity < outBlurbs + ((outBits != 0) ? 1 : 0)) {
			outBlurbs = newCapacity;
			outBits = 0;
			totalBits = newCapacity << 3;
		}
		if (newCapacity < consumedBlurbs + ((consumedBits != 0) ? 1 : 0)) {
			consumedBlurbs = newCapacity;
			consumedBits = 0;
			totalConsumedBits = newCapacity << 3;
		}
		buffer = newBuffer;
		outCapacity = newCapacity;
		return true;
	}

	private boolean grow(int minBlurbsToAdd) {
		int newCapacity = Math.max(outCapacity * 2, outCapacity + minBlurbsToAdd);
		return resize(newCapacity);
	}

	private boolean ensureSize(int bitsToAdd) {
		if ((outCapacity << 3) < totalBits + bitsToAdd) {
			return grow((bitsToAdd >> 3) + 2);
		}
		return true;
	}

	/*
	 * private int readFromStream() throws IOException { // first shift the
	 * unconsumed buffer data toward the front as much as possible if
	 * (totalConsumedBits >= BITS_PER_BLURB) { int l = 0; int r =
	 * consumedBlurbs; int r_end = inBlurbs + ((inBits != 0) ? 1 : 0); for (; r
	 * < r_end; l++, r++) buffer[l] = buffer[r]; for (; l < r_end; l++)
	 * buffer[l] = 0; inBlurbs -= consumedBlurbs; totalBits -= consumedBlurbs <<
	 * 3; consumedBlurbs = 0; totalConsumedBits = consumedBits; }
	 * 
	 * // grow if we need to if (capacity <= 1) resize(16);
	 * 
	 * // set the target for reading, taking into account blurb alignment //
	 * blurb == byte, so no gyrations necessary: int bytes = capacity -
	 * inBlurbs;
	 * 
	 * // finally, read in some data bytes = is.read(buffer, inBlurbs, bytes);
	 * if (bytes <= 0) throw new EOFException();
	 * 
	 * // now we have to handle partial blurb cases: // blurb == byte, so no
	 * gyrations necessary: inBlurbs += bytes; totalBits += bytes << 3; return
	 * bytes; }
	 */

	/**
	 * The constructor.
	 * 
	 * @param os
	 *            The InputStream to read bits from
	 */
	public BitOutputStream(OutputStream os) {
		this.os = os;
	}

	/**
	 * The constructor.
	 */
	public BitOutputStream() {
	}

	/**
	 * TODO
	 */
	// public void clear() {
	//
	// }

	/**
	 * Concatinate one InputBitStream to the end of this one.
	 * 
	 * @param src
	 *            The inputBitStream to copy
	 * @return True if copy was successful
	 */
	public boolean concatenateAligned(BitOutputStream src) {
		int bitsToAdd = src.totalBits - src.totalConsumedBits;
		if (bitsToAdd == 0)
			return true;
		if (outBits != src.consumedBits)
			return false;
		if (!ensureSize(bitsToAdd))
			return false;
		if (outBits == 0) {
			System.arraycopy(src.buffer, src.consumedBlurbs, buffer, outBlurbs, (src.outBlurbs - src.consumedBlurbs + ((src.outBits != 0) ? 1 : 0)));
		} else if (outBits + bitsToAdd > BITS_PER_BLURB) {
			buffer[outBlurbs] <<= (BITS_PER_BLURB - outBits);
			buffer[outBlurbs] |= (src.buffer[src.consumedBlurbs] & ((1 << (BITS_PER_BLURB - outBits)) - 1));
			System.arraycopy(src.buffer, src.consumedBlurbs + 1, buffer, outBlurbs + 11, (src.outBlurbs - src.consumedBlurbs - 1 + ((src.outBits != 0) ? 1 : 0)));
		} else {
			buffer[outBlurbs] <<= bitsToAdd;
			buffer[outBlurbs] |= (src.buffer[src.consumedBlurbs] & ((1 << bitsToAdd) - 1));
		}
		outBits = src.outBits;
		totalBits += bitsToAdd;
		outBlurbs = totalBits / BITS_PER_BLURB;
		return true;
	}

	/**
	 * Reset the read CRC-16 value.
	 * 
	 * @param seed
	 *            The initial CRC-16 value
	 */
	public void resetReadCRC16(short seed) {
		readCRC16 = seed;
	}

	/**
	 * return the read CRC-16 value.
	 * 
	 * @return The read CRC-16 value
	 */
	public short getReadCRC16() {
		return readCRC16;
	}

	/**
	 * return the write CRC-16 value.
	 * 
	 * @return The write CRC-16 value
	 */
	public short getWriteCRC16() {
		return CRC16.calc(buffer, outBlurbs);
	}

	/**
	 * return the write CRC-8 value.
	 * 
	 * @return The write CRC-8 value
	 */
	public byte getWriteCRC8() {
		return CRC8.calc(buffer, outBlurbs);
	}

	/**
	 * Test if the Bit Stream is byte aligned.
	 * 
	 * @return True of bit stream is byte aligned
	 */
	public boolean isByteAligned() {
		return ((outBits & 7) == 0);
	}

	/**
	 * Test if the Bit Stream consumed bits is byte aligned.
	 * 
	 * @return True of bit stream consumed bits is byte aligned
	 */
	public boolean isConsumedByteAligned() {
		return ((consumedBits & 7) == 0);
	}

	/**
	 * return the number of bits to read to align the byte.
	 * 
	 * @return The number of bits to align the byte
	 */
	public int bitsLeftForByteAlignment() {
		return 8 - (consumedBits & 7);
	}

	/**
	 * return the number of bytes left to read.
	 * 
	 * @return The number of bytes left to read
	 */
	public int getInputBytesUnconsumed() {
		return (totalBits - totalConsumedBits) >> 3;
	}

	/**
	 * Write zero bits.
	 * 
	 * @param bits
	 *            The number of zero bits to write
	 * @throws IOException
	 *             On write error
	 */
	public void writeZeroes(int bits) throws IOException {
		if (bits == 0)
			return;
		if (!ensureSize(bits))
			throw new IOException("Memory Allocation Error");
		totalBits += bits;
		while (bits > 0) {
			int n = Math.min(BITS_PER_BLURB - outBits, bits);
			buffer[outBlurbs] <<= n;
			bits -= n;
			outBits += n;
			if (outBits == BITS_PER_BLURB) {
				outBlurbs++;
				outBits = 0;
			}
		}
	}

	/**
	 * Write a true/false integer.
	 * 
	 * @param val
	 *            The true/false value
	 * @param bits
	 *            The bit size to write
	 * @throws IOException
	 *             On write error
	 */
	public void writeRawUInt(boolean val, int bits) throws IOException {
		writeRawUInt((val) ? 1 : 0, bits);
	}

	public void writeRawUInt(int val, int bits) throws IOException {
		if (bits == 0)
			return;

		// inline the size check so we don't incure a function call
		// unnecessarily
		if ((outCapacity << 3) < totalBits + bits) {
			if (!ensureSize(bits))
				throw new IOException("Memory allocation error");
		}

		// zero-out unused bits; WATCHOUT: other code relies on this, so this
		// needs to stay
		if (bits < 32)
			val &= (~(0xffffffff << bits)); // zero-out unused bits
		totalBits += bits;
		while (bits > 0) {
			int n = BITS_PER_BLURB - outBits;
			if (n == BITS_PER_BLURB) { // i.e. outBits == 0
				if (bits < BITS_PER_BLURB) {
					buffer[outBlurbs] = (byte) val;
					outBits = bits;
					break;
				} else if (bits == BITS_PER_BLURB) {
					buffer[outBlurbs++] = (byte) val;
					break;
				} else {
					int k = bits - BITS_PER_BLURB;
					buffer[outBlurbs++] = (byte) (val >> k);

					// we know k < 32 so no need to protect against the gcc bug
					// mentioned above
					val &= (~(0xffffffff << k));
					bits -= BITS_PER_BLURB;
				}
			} else if (bits <= n) {
				buffer[outBlurbs] <<= bits;
				buffer[outBlurbs] |= val;
				if (bits == n) {
					outBlurbs++;
					outBits = 0;
				} else
					outBits += bits;
				break;
			} else {
				int k = bits - n;
				buffer[outBlurbs] <<= n;
				buffer[outBlurbs] |= (val >> k);

				// we know n > 0 so k < 32 so no need to protect against the gcc
				// bug mentioned above
				val &= (~(0xffffffff << k));
				bits -= n;
				outBlurbs++;
				outBits = 0;
			}
		}
	}

	public void writeRawInt(int val, int bits) throws IOException {
		writeRawUInt((int) val, bits);
	}

	public void writeRawULong(long val, int bits) throws IOException {
		if (bits == 0)
			return;
		if (!ensureSize(bits))
			throw new IOException("Memory Allocate Error");
		val &= MASK32[bits];
		totalBits += bits;
		while (bits > 0) {
			if (outBits == 0) {
				if (bits < BITS_PER_BLURB) {
					buffer[outBlurbs] = (byte) val;
					outBits = bits;
					break;
				} else if (bits == BITS_PER_BLURB) {
					buffer[outBlurbs++] = (byte) val;
					break;
				} else {
					int k = bits - BITS_PER_BLURB;
					buffer[outBlurbs++] = (byte) (val >> k);

					// we know k < 64 so no need to protect against the gcc bug
					// mentioned above
					val &= (~(0xffffffffffffffffL << k));
					bits -= BITS_PER_BLURB;
				}
			} else {
				int n = Math.min(BITS_PER_BLURB - outBits, bits);
				int k = bits - n;
				buffer[outBlurbs] <<= n;
				buffer[outBlurbs] |= (val >> k);

				// we know n > 0 so k < 64 so no need to protect against the gcc
				// bug mentioned above
				val &= (~(0xffffffffffffffffL << k));
				bits -= n;
				outBits += n;
				if (outBits == BITS_PER_BLURB) {
					outBlurbs++;
					outBits = 0;
				}
			}
		}
	}

	public void writeRawUIntLittleEndian(int val) throws IOException {
		// NOTE: we rely on the fact that write_raw_uint32() masks out the
		// unused bits
		writeRawUInt(val, 8);
		writeRawUInt(val >> 8, 8);
		writeRawUInt(val >> 16, 8);
		writeRawUInt(val >> 24, 8);
	}

	public void writeByteBlock(byte[] vals, int nvals) throws IOException {
		// this could be faster but currently we don't need it to be
		for (int i = 0; i < nvals; i++) {
			writeRawUInt((int) (vals[i]), 8);
		}
	}

	public void writeUnaryUnsigned(int val) throws IOException {
		if (val < 32)
			writeRawUInt(1, ++val);
		else if (val < 64)
			writeRawULong(1, ++val);
		else {
			writeZeroes(val);
			writeRawUInt(1, 1);
		}
	}

	public int riceBits(int val, int parameter) {
		int msbs, uval;
		// fold signed to unsigned
		if (val < 0) {
			// equivalent to (unsigned)(((--val) < < 1) - 1); but without the
			// overflow problem at MININT
			uval = (int) (((-(++val)) << 1) + 1);
		} else {
			uval = (int) (val << 1);
		}
		msbs = uval >> parameter;
		return 1 + parameter + msbs;
	}
	/*
	 * DRR FIX # ifdef SYMMETRIC_RICE boolean
	 * write_symmetric_rice_signed(BitBuffer8 * bb, int val, unsigned parameter)
	 * { unsigned total_bits, interesting_bits, msbs; uint32 pattern;
	 * 
	 * ASSERT(0 != bb); ASSERT(0 != buffer); ASSERT(parameter <= 31); // init
	 * pattern with the unary end bit and the sign bit if (val < 0) { pattern =
	 * 3; val = -val; } else pattern = 2;
	 * 
	 * msbs = val >> parameter; interesting_bits = 2 + parameter; total_bits =
	 * interesting_bits + msbs; pattern < <= parameter; pattern |= (val & ((1 <
	 * < parameter) - 1)); // the binary LSBs
	 * 
	 * if (total_bits <= 32) { if (!write_raw_uint32(bb, pattern, total_bits))
	 * return false; } else { // write the unary MSBs if (!write_zeroes(bb,
	 * msbs)) return false; // write the unary end bit, the sign bit, and binary
	 * LSBs if (!write_raw_uint32(bb, pattern, interesting_bits)) return false;
	 * } return true; }
	 * 
	 * boolean write_symmetric_rice_signed_escape(BitBuffer8 * bb, int val,
	 * unsigned parameter) { unsigned total_bits, val_bits; uint32 pattern;
	 * 
	 * ASSERT(0 != bb); ASSERT(0 != buffer); ASSERT(parameter <= 31);
	 * 
	 * val_bits = bitmath_silog2(val); total_bits = 2 + parameter + 5 +
	 * val_bits;
	 * 
	 * if (total_bits <= 32) { pattern = 3; pattern < <= (parameter + 5);
	 * pattern |= val_bits; pattern < <= val_bits; pattern |= (val & ((1 < <
	 * val_bits) - 1)); if (!write_raw_uint32(bb, pattern, total_bits)) return
	 * false; } else { // write the '-0' escape code first if
	 * (!write_raw_uint32(bb, 3 u < < parameter, 2 + parameter)) return false;
	 * // write the length if (!write_raw_uint32(bb, val_bits, 5)) return false;
	 * // write the value if (!write_raw_int32(bb, val, val_bits)) return false;
	 * } return true; } # endif // ifdef SYMMETRIC_RICE
	 */

	public void writeRiceSigned(int val, int parameter) throws IOException {
		int totalBits;
		int interestingBits;
		int msbs;
		int uval;
		int pattern;

		// fold signed to unsigned
		if (val < 0) {
			// equivalent to (unsigned)(((--val) < < 1) - 1); but without the
			// overflow problem at MININT
			uval = (int) (((-(++val)) << 1) + 1);
		} else {
			uval = (int) (val << 1);
		}
		msbs = uval >> parameter;
		interestingBits = 1 + parameter;
		totalBits = interestingBits + msbs;
		pattern = 1 << parameter; /* the unary end bit */
		pattern |= (uval & ((1 << parameter) - 1)); /* the binary LSBs */
		if (totalBits <= 32) {
			writeRawUInt(pattern, totalBits);
		} else {
			/* write the unary MSBs */
			writeZeroes(msbs);
			/* write the unary end bit and binary LSBs */
			writeRawUInt(pattern, interestingBits);
		}
	}

	public void writeUTF8UInt(int val) throws IOException {
		if (val < 0x80) {
			writeRawUInt(val, 8);
		} else if (val < 0x800) {
			writeRawUInt(0xC0 | (val >> 6), 8);
			writeRawUInt(0x80 | (val & 0x3F), 8);
		} else if (val < 0x10000) {
			writeRawUInt(0xE0 | (val >> 12), 8);
			writeRawUInt(0x80 | ((val >> 6) & 0x3F), 8);
			writeRawUInt(0x80 | (val & 0x3F), 8);
		} else if (val < 0x200000) {
			writeRawUInt(0xF0 | (val >> 18), 8);
			writeRawUInt(0x80 | ((val >> 12) & 0x3F), 8);
			writeRawUInt(0x80 | ((val >> 6) & 0x3F), 8);
			writeRawUInt(0x80 | (val & 0x3F), 8);
		} else if (val < 0x4000000) {
			writeRawUInt(0xF8 | (val >> 24), 8);
			writeRawUInt(0x80 | ((val >> 18) & 0x3F), 8);
			writeRawUInt(0x80 | ((val >> 12) & 0x3F), 8);
			writeRawUInt(0x80 | ((val >> 6) & 0x3F), 8);
			writeRawUInt(0x80 | (val & 0x3F), 8);
		} else {
			writeRawUInt(0xFC | (val >> 30), 8);
			writeRawUInt(0x80 | ((val >> 24) & 0x3F), 8);
			writeRawUInt(0x80 | ((val >> 18) & 0x3F), 8);
			writeRawUInt(0x80 | ((val >> 12) & 0x3F), 8);
			writeRawUInt(0x80 | ((val >> 6) & 0x3F), 8);
			writeRawUInt(0x80 | (val & 0x3F), 8);
		}
	}

	public void writeUTF8ULong(long val) throws IOException {
		if (val < 0x80) {
			writeRawUInt((int) val, 8);
		} else if (val < 0x800) {
			writeRawUInt(0xC0 | (int) (val >> 6), 8);
			writeRawUInt(0x80 | (int) (val & 0x3F), 8);
		} else if (val < 0x10000) {
			writeRawUInt(0xE0 | (int) (val >> 12), 8);
			writeRawUInt(0x80 | (int) ((val >> 6) & 0x3F), 8);
			writeRawUInt(0x80 | (int) (val & 0x3F), 8);
		} else if (val < 0x200000) {
			writeRawUInt(0xF0 | (int) (val >> 18), 8);
			writeRawUInt(0x80 | (int) ((val >> 12) & 0x3F), 8);
			writeRawUInt(0x80 | (int) ((val >> 6) & 0x3F), 8);
			writeRawUInt(0x80 | (int) (val & 0x3F), 8);
		} else if (val < 0x4000000) {
			writeRawUInt(0xF8 | (int) (val >> 24), 8);
			writeRawUInt(0x80 | (int) ((val >> 18) & 0x3F), 8);
			writeRawUInt(0x80 | (int) ((val >> 12) & 0x3F), 8);
			writeRawUInt(0x80 | (int) ((val >> 6) & 0x3F), 8);
			writeRawUInt(0x80 | (int) (val & 0x3F), 8);
		} else if (val < 0x80000000) {
			writeRawUInt(0xFC | (int) (val >> 30), 8);
			writeRawUInt(0x80 | (int) ((val >> 24) & 0x3F), 8);
			writeRawUInt(0x80 | (int) ((val >> 18) & 0x3F), 8);
			writeRawUInt(0x80 | (int) ((val >> 12) & 0x3F), 8);
			writeRawUInt(0x80 | (int) ((val >> 6) & 0x3F), 8);
			writeRawUInt(0x80 | (int) (val & 0x3F), 8);
		} else {
			writeRawUInt(0xFE, 8);
			writeRawUInt(0x80 | (int) ((val >> 30) & 0x3F), 8);
			writeRawUInt(0x80 | (int) ((val >> 24) & 0x3F), 8);
			writeRawUInt(0x80 | (int) ((val >> 18) & 0x3F), 8);
			writeRawUInt(0x80 | (int) ((val >> 12) & 0x3F), 8);
			writeRawUInt(0x80 | (int) ((val >> 6) & 0x3F), 8);
			writeRawUInt(0x80 | (int) (val & 0x3F), 8);
		}
	}

	/**
	 * Write zero bits to byte boundry.
	 * 
	 * @throws IOException
	 *             On error writing to bit stream
	 */
	public void zeroPadToByteBoundary() throws IOException {
		// 0-pad to byte boundary
		if ((outBits & 7) != 0)
			writeZeroes(8 - (outBits & 7));
	}

	/**
	 * Flush bit stream after aligning byte boundry.
	 * 
	 * @throws IOException
	 *             On error writing.
	 */
	public void flushByteAligned() throws IOException {
		zeroPadToByteBoundary();
		if (outBlurbs == 0)
			return;
		os.write(buffer, 0, outBlurbs);
		outBlurbs = 0;
	}

	/*
	 * DRR FIX boolean peek_bit(unsigned * val, boolean(* read_callback) (byte
	 * buffer[], unsigned * bytes, void * client_data), void * client_data) {
	 * 
	 * while (1) { if (total_consumed_bits < total_bits) { val =
	 * (buffer[consumed_blurbs] & BLURB_BIT_TO_MASK(consumed_bits)) ? 1 : 0;
	 * return true; } else { if (!read_from_client_(bb, read_callback,
	 * client_data)) return false; } } }
	 */

	/*
	 * # ifdef SYMMETRIC_RICE boolean read_symmetric_rice_signed( BitBuffer8 *
	 * bb, int * val, unsigned parameter, boolean(* read_callback) (byte
	 * buffer[], unsigned * bytes, void * client_data), void * client_data) {
	 * uint32 sign = 0, lsbs = 0, msbs = 0;
	 * 
	 * ASSERT(0 != bb); ASSERT(0 != buffer); ASSERT(parameter <= 31); // read
	 * the unary MSBs and end bit if (!read_unary_unsigned(bb, & msbs,
	 * read_callback, client_data)) return false; // read the sign bit if
	 * (!read_bit_to_uint32(bb, & sign, read_callback, client_data)) return
	 * false; // read the binary LSBs if (!read_raw_uint32(bb, & lsbs,
	 * parameter, read_callback, client_data)) return false; // compose the
	 * value val = (msbs < < parameter) | lsbs; if (sign) val = - (* val);
	 * 
	 * return true; } # endif // ifdef SYMMETRIC_RICE
	 * 
	 * boolean read_rice_signed( BitBuffer8 * bb, int * val, unsigned parameter,
	 * boolean(* read_callback) (byte buffer[], unsigned * bytes, void *
	 * client_data), void * client_data) { uint32 lsbs = 0, msbs = 0; unsigned
	 * uval;
	 * 
	 * ASSERT(0 != bb); ASSERT(0 != buffer); ASSERT(parameter <= 31); // read
	 * the unary MSBs and end bit if (!read_unary_unsigned(bb, & msbs,
	 * read_callback, client_data)) return false; // read the binary LSBs if
	 * (!read_raw_uint32(bb, & lsbs, parameter, read_callback, client_data))
	 * return false; // compose the value uval = (msbs < < parameter) | lsbs; if
	 * (uval & 1) val = - ((int) (uval >> 1)) - 1; else val = (int) (uval >> 1);
	 * 
	 * return true; }
	 */

	/**
	 * Returns the totalBits.
	 * 
	 * @return Returns the totalBits.
	 */
	public int getTotalBits() {
		return totalBits;
	}

	/**
	 * Returns the totalBlurbs.
	 * 
	 * @return Returns the totalBlurbs.
	 */
	public int getTotalBlurbs() {
		return (totalBits + 7) / 8;
	}
}
